import socket
import sys
from utils import *
from oppEval import *
from preflop import *
from postflop import *
from handEval import *

"""
Simple example pokerbot, written in python. This is an example of a bare bones,
dumb pokerbot - it only sets up the socket necessary to connect with the engine
and then always returns the same action. It is meant as an example of how a
pokerbot should communicate with the engine.
"""

action = None
decision = None

# port number specified by the engine to connect to.
def connect(port = int(sys.argv[1])):
    global action
    if __name__ == "__main__":
        # port number specified by the engine to connect to.
        port = int(sys.argv[1])
        # connect the socket to the engine
        s = socket.create_connection(('localhost', int(sys.argv[1])))
        # Get a file-object for reading packets from the socket.
        # Using this ensures that you get exactly one packet per read.
        f_in = s.makefile()
        while 1:
            # block until the engine sends us a packet
            data = f_in.readline()
            # if we receive a null return, then the connection is dead
            if not data:
                print "Gameover, engine disconnected"
                break
            # Here is where you should implement code to parse the packets from
            # the engine and act on it.
            #print data
            action = 'CHECK\n'
            parse(data)
            # When appropriate, reply to the engine with a legal action.
            # The engine will ignore all spurious packets you send.
            # The engine will also check/fold for you if you return an
            # illegal action.
            # When sending responses, you need to have a newline character (\n) or
            # carriage return (\r), or else your bot will hang!
            #Player.me.hand.write()
            if data.split()[0] == "GETACTION":
                #print action
                s.send(action)
        # if we get here, the server disconnected us, so clean up the socket
        s.close()
 
def parse(s):
    global action
    data = s.split(' ')
    if data[0] == "NEWGAME":
        Game.newGame(data)
    elif data[0] == "NEWHAND":
        Game.newHand(data)
    elif data[0] == "GETACTION":
        getAction(data)
        action = playGame() + '\n'
        Game.lastStreet = Game.street
    elif data[0] == "HANDOVER":
        handOver(data)

def getAction(data):
    index = 2
    numBoardCards = int(data[index])
    if numBoardCards > 0:
        community = Hand([parseCard(card) for card in data[index + 1].split(',')])
        index += 1
    index += 1
    numLastActions = int(data[index])
    if numLastActions > 0:
        lastActions = data[index + 1].split(',')
    else:
        lastActions = []
    # Perform all actions in last actions list
    for lastAction in lastActions:
        action = Action(lastAction)
        action.perform()
        if action.player in [Player.leftOpp, Player.rightOpp] and Game.street > 0 and Game.street < 4:
            updateWithAction(action.player, action)
        elif action.isType('DEAL'):
            Game.community = community
            if Game.street == 1:
                updateWithFlop(Player.leftOpp)
                updateWithFlop(Player.rightOpp)
                if not Player.leftOpp.folded:
                    Player.leftOpp.sawFlopCount += 1
                if not Player.rightOpp.folded:
                    Player.rightOpp.sawFlopCount += 1
            if Game.street >= 1:
                handEval(Player.me)
    Player.me.time = float(data[len(data) - 1])
    
def handOver(data):
    global lastActions, time, playersLeft
    numLastActions = int(data[4])
    if numLastActions > 0:
        actions = data[5].split(',')
    else:
        actions = []
    for action in actions:
        Action(action).perform()
    Player.me.time = float(data[len(data) - 1])
    postHandEval()

def postHandEval():
    for player in [Player.leftOpp, Player.rightOpp]:
        addPreflopActions(player, player.actions[0])
        for i in range(1, 4):
            addPostflopActions(player, player.actions[i])
        if player.hand.size() > 0:
            # Preflop hand strength
            strength = preflopStrength(player.hand)
            addPreflopStrengths(player, strength)
            # Postflop hand strength 
            strength = postflopStrength(player.hand, Hand(Game.community.cards[0:3]), 1)
            addPostflopStrengths(player, strength)
            for i in range(4, 6):
                strength = postflopStrength(player.hand, Hand(Game.community.cards[0:i]), i - 2)
                addPostflopStrengths(player, strength)
        else:
            addPreflopStrengths(player, None)
            for i in range(3):
                addPostflopStrengths(player, None)
        adjPreflopBehavior(player)
        adjPostflopBehavior(player)

def playGame():
    global decision
    strategize(Player.me)
    if Game.street == 0:
        if Game.street != Game.lastStreet:
            decision = preflopBot(Player.me)
        return preflopMakeDecision(decision)
    elif Game.street >= 1:
        #print sum(Player.rightOpp.dist[:5]), sum(Player.rightOpp.dist[:10]), sum(Player.rightOpp.dist[:20]), sum(Player.rightOpp.dist[:30])
        decision = postflopBot(Player.me)
        return postflopMakeDecision(decision)

def preflopMakeDecision(decision):
    betAmt = decision[1] * Game.sb
    if betAmt > Pot.toCall(Player.me) and Pot.toCall(Player.me) < Player.me.stack:
        if Pot.raiseCounts[Player.me] == 0:
            if Player.me.absHS > 0.06:
                decision = (decision[0], 0, decision[2])
            return 'RAISE:' + str(legalBet(betAmt))
        elif Pot.raiseCounts[Player.me] >= 1 and Player.me.absHS <= 0.06:
            return 'RAISE:' + str(legalBet(100 * Game.bb))
    if Pot.toCall(Player.me) == 0:
        return 'CHECK'
    if Pot.toCall(Player.me) <= 2 * Game.sb and decision[0]:
        return legalCall()
    if Pot.toCall(Player.me) <= decision[2]:
        return legalCall()
    return 'FOLD'

def postflopMakeDecision(decision):
    decision = scaleDecision(decision)
    betAmt = int(decision[0] * Pot.size) * Game.sb
    if betAmt > Pot.toCall(Player.me) and Pot.toCall(Player.me) < Player.me.stack:
        decision = (0, decision[1])
        if Pot.raiseCounts[Player.me] <= 1:
            if Pot.toCall(Player.me) == 0:
                return 'BET:' + str(legalBet(betAmt))
            elif Pot.toCall(Player.me) > 0:
                return 'RAISE:' + str(legalBet(betAmt))
    if Pot.toCall(Player.me) == 0:
        return 'CHECK'
    if Pot.size >= 30 and float(Pot.toCall(Player.me)) / Pot.size <= .06:
        return legalCall()
    if float(Pot.size) / Pot.toCall(Player.me) >= decision[1]:
        return legalCall()
    return 'FOLD'

def scaleDecision(decision):
    raiseAmt, callUpTo = decision
    # Scale % of pot to call
    if Pot.size <= 12 * Game.sb:
        raiseAmt *= 1.4
    elif Pot.size <= 20 * Game.sb:
        raiseAmt *= 1.2
    if Pot.size >= 90 * Game.sb:
        raiseAmt *= .8
    # Scale pot odds to call
    if Pot.size <= 15 * Game.sb:
        callUpTo *= .5
    #print 'raiseamt: ' + str(raiseAmt), 'callupto:' + str(callUpTo)
    return (raiseAmt, callUpTo)

def legalBet(bet):
    # Bet at least min raise amt
    #print 'minraiseamt: ' + str(Pot.minRaiseAmt), 'maxbet: ' + str(max(Pot.bets.values())), bet
    bet = max(Pot.minRaiseAmt + max(Pot.bets.values()), bet)
    # If not enough chips, go all in
    if bet > Player.me.stack:
        #print 'going allin', 'mybet: ' + str(Pot.bets[Player.me]), 'stack: ' + str(Player.me.stack)
        bet = Pot.bets[Player.me] + Player.me.stack
    return bet

def legalCall():
    if Pot.toCall(Player.me) > Player.me.stack:
        return 'RAISE:' + str(Pot.bets[Player.me] + Player.me.stack)
    else:
        return 'CALL'

connect()
